package com.zrq.secondkill.controller;

import com.google.common.util.concurrent.RateLimiter;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.zrq.secondkill.constant.Constant;
import com.zrq.secondkill.error.BusinessErrorEnum;
import com.zrq.secondkill.error.BusinessException;
import com.zrq.secondkill.response.CommonReturnType;
import com.zrq.secondkill.service.ItemService;
import com.zrq.secondkill.service.OrderService;
import com.zrq.secondkill.service.PromoService;
import com.zrq.secondkill.service.SessionService;
import com.zrq.secondkill.service.model.UserModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.concurrent.*;

/**
 * @author zrq
 */
@Slf4j
@RestController
@RequestMapping("/order")
@CrossOrigin(origins = {"*"}, allowCredentials = "true")
public class OrderController {
    private final OrderService orderService;

    private final SessionService sessionService;

    private final PromoService promoService;

    private final RedisTemplate redisTemplate;

    private final ItemService itemService;

    private ExecutorService executorService;

    private RateLimiter creataOrderRateLimiter;

    @Value("${rate-limit.rate}")
    private Integer createOrderLimitRate;

    @Value("${rate-limit.wait-time}")
    private Integer createOrderLimitWaitTime;

    @Value("${queue.thread-pool-size}")
    private Integer threadPoolSize;

    public OrderController(OrderService orderService, SessionService sessionService, PromoService promoService, RedisTemplate redisTemplate, ItemService itemService) {
        this.orderService = orderService;
        this.sessionService = sessionService;
        this.promoService = promoService;
        this.redisTemplate = redisTemplate;
        this.itemService = itemService;
    }

    @PostConstruct
    public void init() {
        executorService = new ThreadPoolExecutor(
                threadPoolSize,
                threadPoolSize,
                1,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(),
                new ThreadFactoryBuilder().setNameFormat("createOrderThreadPool-%d").build()
        );

        creataOrderRateLimiter=RateLimiter.create(createOrderLimitRate);
    }

    @PostMapping("/createorder")
    public CommonReturnType createOrder(@RequestParam("itemId") String itemId,
                                        @RequestParam("amount") Integer amount,
                                        @RequestParam(name = "promoId", required = false) Integer promoId,
                                        @RequestParam("token") String token,
                                        @RequestParam("promoToken") String promoToken) throws BusinessException {

        //验证登录
        //TODO(zrq):用户登录验证应当利用拦截器完成
        Boolean isLogin = (Boolean) sessionService.getAttribute(token, "isLogin");
        if (isLogin == null) {
            throw new BusinessException(BusinessErrorEnum.USER_NOT_LOGIN);
        }

        UserModel userModel = (UserModel) sessionService.getAttribute(token, "userModel");

        //验证秒杀令牌
        if (!promoService.validatePromoItemToken(promoToken, userModel.getId(), Integer.valueOf(itemId))) {
            return CommonReturnType.create("failed", "下单失败,秒杀令牌校验不通过");
        }

        //限流
        if(!creataOrderRateLimiter.tryAcquire(1,1,TimeUnit.SECONDS)){
            return CommonReturnType.create("failed", "下单失败,系统限流机制");
        }

        //对耗时的实际下单操作进行队列泄洪
        Future<Object> future=executorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                return orderService.asyncCreateOrder(userModel.getId(), Integer.valueOf(itemId), amount, promoId);
            }
        });

        Boolean success;
        try {
            success= (Boolean) future.get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("下单线程池异常",e);
            return CommonReturnType.create("failed", "下单失败");
        }
//        Boolean success = orderService.asyncCreateOrder(userModel.getId(), Integer.valueOf(itemId), amount, promoId);

//        return CommonReturnType.create("下单成功");

        if (success) {
            return CommonReturnType.create("下单成功");
        } else {
            return CommonReturnType.create("failed", "下单失败");
        }

    }

    @PostMapping("/getpromotoken")
    public CommonReturnType getPromoToken(@RequestParam("itemId") String itemId,
                                          @RequestParam("amount") Integer amount,
                                          @RequestParam(name = "promoId", required = false) Integer promoId,
                                          @RequestParam("token") String token) throws BusinessException {

        //检查售罄标识
        Object flag = redisTemplate.opsForValue().get(Constant.SOLD_OUT_PREFIX + itemId);
        if (flag != null) {
            return CommonReturnType.create("failed", "库存售罄,获取秒杀令牌失败");
        }

        //验证登录
        //TODO(zrq):用户登录验证应当利用拦截器完成
        Boolean isLogin = (Boolean) sessionService.getAttribute(token, "isLogin");
        if (isLogin == null) {
            throw new BusinessException(BusinessErrorEnum.USER_NOT_LOGIN);
        }

        UserModel userModel = (UserModel) sessionService.getAttribute(token, "userModel");

        String promoToken = promoService.generatePromoItemToken(userModel.getId(), Integer.valueOf(itemId), promoId);

        return CommonReturnType.create(promoToken);
    }
}
